package com.cnebula.openapi.auth.service.impl;

import com.cnebula.openapi.auth.AuthOptions;
import com.cnebula.openapi.auth.exception.ClientAppAddressMismatch;
import com.cnebula.openapi.auth.exception.ClientAppServiceRequestFail;
import com.cnebula.openapi.auth.exception.ClientKeyExpireException;
import com.cnebula.openapi.auth.exception.ClientKeyNoExistException;
import com.cnebula.openapi.auth.exception.ClientKeyUnavailableExceptoin;
import com.cnebula.openapi.auth.service.ClientAppService;
import com.cnebula.openapi.auth.vo.ClientApp;

import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.net.SocketAddress;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;

public class ClientAppServiceImpl implements ClientAppService {

	WebClient client;
	AuthOptions options;

	private final String LSP_CLIENT_KEY_QUERY_URI = "/clientkey/client_id";
	
	private static final Logger log = LoggerFactory.getLogger(ClientAppServiceImpl.class);

	public ClientAppServiceImpl(Vertx vertx, AuthOptions options) {
		this.options = options;
		client = WebClient.create(vertx);
	}

	@Override
	public Future<ClientApp> checkKey(String clientKey, String tenantId, SocketAddress clientAddress) {
		// 1. 查询 client key 信息

		// 2. 验证 client key 信息的有效性

		// 3. 返回

		Future<ClientApp> future = Future.future();
		String logTag = "request [http://"+options.getOkapiHost()+":"+options.getOkapiPort()+LSP_CLIENT_KEY_QUERY_URI + "/" + clientKey+"]";
		this.getClientApp(clientKey, tenantId).setHandler(h -> {
			if (h.succeeded()) {
				ClientApp clientApp = h.result();
				log.info("expect client address :"+clientApp.appUrl+", actual client address:"+clientAddress);
				//验证 client key 信息的有效性
				long expire = Long.parseLong(clientApp.expireDate);
				if (!clientApp.status.equals("1")) {
					future.fail(new ClientKeyUnavailableExceptoin());
				} else if (System.currentTimeMillis() > expire) {
					future.fail(new ClientKeyExpireException());
				} else if (!clientApp.appUrl.contains(clientAddress.host())) {
					future.fail(new ClientAppAddressMismatch());
				} else {
					future.complete(clientApp);
				}
				
			} else {
				log.error(logTag, h.cause());
				future.fail(new ClientAppServiceRequestFail(h.cause().getMessage()));
			}
		});

		return future;
	}

	@Override
	public Future<ClientApp> getClientApp(String clientKey, String tenantId) {
		Future<ClientApp> future = Future.future();
		String logTag = "request [http://"+options.getOkapiHost()+":"+options.getOkapiPort()+LSP_CLIENT_KEY_QUERY_URI + "/" + clientKey+"]";
		
		client.get(options.getOkapiPort(), options.getOkapiHost(), LSP_CLIENT_KEY_QUERY_URI + "/" + clientKey)
				.putHeader("accept", "application/json")
				.putHeader("accept", "text/plain")
				.putHeader("content-type", "application/json")
				.putHeader(AuthOptions.OKAPI_TENANT, tenantId)
				.send(h -> {
					
					if (h.succeeded()) {
						HttpResponse<Buffer> response = h.result();
						String rst = response.bodyAsString();
						int statusCode = response.statusCode();
						if(statusCode == 200){
							JsonObject obj = new JsonObject(rst);
							ClientApp clientApp = ClientApp.build(obj);
							future.complete(clientApp);
						}else if(statusCode == 404) {
							future.fail(new ClientKeyNoExistException());
						}else{
							log.error(logTag+" return http status code:"+response.statusCode()+", message:"+rst);
							future.fail(new ClientAppServiceRequestFail(rst));
						}
					} else {
						log.error(logTag, h.cause());
						future.fail(new ClientAppServiceRequestFail(h.cause().getMessage()));
					}
				});
		return future;
	}

}
